package com.goudan.demo.base.dao;

import com.goudan.demo.util.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;


@Slf4j
@Service("sequenceDao")
public class SequenceDao {

    public SequenceDao() {
    }

    @Transactional(
            propagation = Propagation.REQUIRES_NEW
    )

    public long getSequenceValue(String sequenceName) {
        return this.getSequenceValue(sequenceName, 10, 200);
    }

    private Map<String, Long> querySequence(Connection connection, String sequenceName) throws SQLException {
        PreparedStatement ps = null;
        ResultSet rs = null;
        HashMap resultMap = new HashMap();

        HashMap var16;
        try {
            log.debug("开始从数据库取分段流水[" + sequenceName + "]");
            ps = connection.prepareStatement("SELECT SEQUENCE_VALUE, MAX_VALUE, SEQUENCE_LEN, INCREMENT, START_VALUE  from abk.sequence_info where SEQUENCE_NAME=? ");
            ps.setString(1, sequenceName);
            rs = ps.executeQuery();
            if (!rs.next()) {
                resultMap.put("seqCurrValue", -1L);
                HashMap var20 = resultMap;
                return var20;
            }

            long seqCurrValue = rs.getLong(1);
            long maxValue = rs.getLong(2);
            long seqLen = rs.getLong(3);
            long increment = rs.getLong(4);
            long startValue = rs.getLong(5);
            resultMap.put("seqCurrValue", seqCurrValue);
            resultMap.put("maxValue", maxValue);
            resultMap.put("seqLen", seqLen);
            resultMap.put("startValue", startValue);
            resultMap.put("increment", increment);
            log.debug("取分段流水[{}]完成,{}", sequenceName, resultMap);
            var16 = resultMap;
        } finally {
            if (rs != null) {
                rs.close();
            }

            if (ps != null) {
                ps.close();
            }

        }
        return var16;
    }

    private void insertSequence(Connection connection, String sequenceName) throws SQLException {
        PreparedStatement ps = null;

        try {
            ps = connection.prepareStatement("INSERT INTO abk.sequence_info VALUES(?,?,?,?,?,?,CURRENT_TIMESTAMP)");
            ps.setString(1, sequenceName);
            ps.setLong(2, 10L);
            ps.setLong(3, 999999L);
            ps.setInt(4, 6);
            ps.setInt(5, 1);
            ps.setInt(6, 10);
            ps.execute();
            log.debug("新增流水号[{}]，成功", sequenceName);
        } finally {
            if (ps != null) {
                ps.close();
            }

        }

    }

    public long getSequenceValue(String sequenceName, int tryCnt, int tryIntervalMs) {
        SqlSessionFactory sqlSessionFactory = (SqlSessionFactory) SpringContextHolder.getBean("sqlSessionFactory");
        SqlSession sqlSession = sqlSessionFactory.openSession(false);
        Connection conn = sqlSession.getConnection();

        try {
            conn.setAutoCommit(false);
        } catch (SQLException var18) {
            log.error("关闭连接自动事务提交异常!");
        }

        try {
            long seqCurrValue;
            try {
                seqCurrValue = this.getSeqCurrValue(conn, sequenceName);
                conn.commit();
                seqCurrValue = seqCurrValue;
                return seqCurrValue;
            } catch (Exception var20) {
                int counter = 1;

                while (counter <= tryCnt) {
                    try {
                        Thread.sleep((long) tryIntervalMs);
                        log.warn("重试：取分段流水号，流水名称：{}, 重试第 {} 次获取.", sequenceName, counter);
                        seqCurrValue = this.getSeqCurrValue(conn, sequenceName);
                        conn.commit();
                        long var11 = seqCurrValue;
                        return var11;
                    } catch (Exception var19) {
                        log.warn("取分段流水出现异常，流水名称：{}, 第 {} 次获取.", new Object[]{sequenceName, counter, var19});
                        ++counter;
                    }
                }

                log.warn("取分段流水出现异常，流水名称：{}.", sequenceName, var20);
                throw new RuntimeException("obtain sequence error[" + sequenceName + "], try obtain cnt is[" + tryCnt + "], try obtain interval ms is[" + tryIntervalMs + "]");
            }
        } finally {
            sqlSession.close();
        }
    }

    private void updateSequence(Connection connection, String sequenceName, Map<String, Long> resultMap) throws SQLException {
        PreparedStatement ps = null;

        try {
            ps = connection.prepareStatement("UPDATE abk.sequence_info set SEQUENCE_VALUE=?,UPDATE_TIME=CURRENT_TIMESTAMP WHERE SEQUENCE_NAME=? AND SEQUENCE_VALUE=?");
            long seqCurrValue = (Long) resultMap.get("seqCurrValue");
            long maxValue = (Long) resultMap.get("maxValue");
            long seqLen = (Long) resultMap.get("seqLen");
            long increment = (Long) resultMap.get("increment");
            long startValue = (Long) resultMap.get("startValue");
            long seqNextValue = seqCurrValue + increment;
            if (seqNextValue >= maxValue || (double) seqNextValue > Math.pow(10.0D, (double) seqLen) - 1.0D) {
                seqNextValue = startValue;
            }
            ps.setLong(1, seqNextValue);
            ps.setString(2, sequenceName);
            ps.setLong(3, seqCurrValue);
            int cnt;
            synchronized (this) {
                cnt = ps.executeUpdate();
                connection.commit();
            }

            log.debug("更新流水号sequenceName[{}], seqCurrValue[{}], seqNextValue[{}]", new Object[]{sequenceName, seqCurrValue, seqNextValue});
            if (cnt != 1 || ps.getUpdateCount() != 1) {
                log.info("更新流水号[{}]信息失败，返回更新记录数{}", sequenceName, cnt);
                throw new RuntimeException("update sequence error[" + sequenceName + "]");
            }
        } finally {
            if (ps != null) {
                ps.close();
            }

        }

    }

    public long getSeqCurrValue(Connection conn, String sequenceName) throws SQLException {
        Map<String, Long> resultMap = this.querySequence(conn, sequenceName);
        long seqCurrValue = resultMap.get("seqCurrValue");
        if (seqCurrValue == -1L) {
            this.insertSequence(conn, sequenceName);
            resultMap = this.querySequence(conn, sequenceName);
            seqCurrValue = (Long) resultMap.get("seqCurrValue");
        }

        this.updateSequence(conn, sequenceName, resultMap);
        return seqCurrValue;
    }
}
